from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QPushButton, QLabel, QFileDialog, QSplitter,
                             QSizePolicy, QMessageBox, QDialog, QDialogButtonBox,
                             QListWidget, QListWidgetItem,QFrame)  # 新增QListWidget相关导入
from PyQt5.QtCore import Qt
from core import PointCloudViewer, CompletionThread, SegmentationThread, ReconstructionThread
from core.segmentation_methods import SegmentationMethod  # 导入分割方法枚举
from core.reconstruction_methods import ReconstructionMethod  # 导入重建方法枚举
from ui.success_dialog import SuccessDialog
from ui.waiting_dialog import WaitingDialog
from config import WINDOW_SIZE
import numpy as np
from PyQt5.QtGui import QIcon, QPixmap



class SuccessLoadDialog(QDialog):
    def __init__(self, point_cloud_count, file_ext, single_pc_count, parent=None):
        super().__init__(parent)
        self.setWindowTitle("加载成功")
        self.setFixedSize(400, 220)  # 固定大小，避免变形
        self.setWindowIcon(QIcon("icons/success.png"))  # 可选：添加图标（需自行准备）
        self.setStyleSheet("""
            QDialog {
                background-color: #f8f9fa;
                border-radius: 10px;
                border: 1px solid #e0e0e0;
            }
            QLabel#title {
                font-size: 18px;
                font-weight: bold;
                color: #2d3748;
            }
            QLabel#content {
                font-size: 14px;
                color: #4a5568;
                line-height: 1.6;
            }
            QPushButton {
                background-color: #4299e1;
                color: white;
                border: none;
                border-radius: 6px;
                padding: 8px 24px;
                font-size: 14px;
            }
            QPushButton:hover {
                background-color: #3182ce;
            }
            QPushButton:pressed {
                background-color: #2b6cb0;
            }
        """)

        # 布局：左侧图标 + 右侧文本 + 底部按钮
        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(20, 20, 20, 20)
        main_layout.setSpacing(15)

        # 顶部：图标 + 文本
        top_layout = QHBoxLayout()
        top_layout.setSpacing(15)

        # 成功图标（使用Qt内置图标，无需额外文件）
        icon_label = QLabel()
        pixmap = QPixmap.fromImage(QIcon.fromTheme("dialog-information").pixmap(64, 64).toImage())
        icon_label.setPixmap(pixmap)
        top_layout.addWidget(icon_label, alignment=Qt.AlignTop)

        # 文本内容
        text_layout = QVBoxLayout()
        text_layout.setSpacing(8)

        title_label = QLabel("点云加载成功！")
        title_label.setObjectName("title")
        text_layout.addWidget(title_label)

        content_text = f"""
        共加载 {point_cloud_count} 个点云文件
        格式：.{file_ext}
        当前点云点数：{single_pc_count} 个
        """
        content_label = QLabel(content_text)
        content_label.setObjectName("content")
        content_label.setWordWrap(True)  # 自动换行
        text_layout.addWidget(content_label)

        top_layout.addLayout(text_layout)
        main_layout.addLayout(top_layout)

        # 分割线
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setStyleSheet("background-color: #e0e0e0;")
        main_layout.addWidget(line)

        # 底部：确认按钮（居中）
        btn_layout = QHBoxLayout()
        confirm_btn = QPushButton("确定")
        confirm_btn.clicked.connect(self.accept)
        btn_layout.addWidget(confirm_btn, alignment=Qt.AlignCenter)
        main_layout.addLayout(btn_layout)

# 自定义重建成功对话框
class SuccessReconstructDialog(QDialog):
    def __init__(self, method_name, vertex_count, triangle_count, parent=None):
        super().__init__(parent)
        self.setWindowTitle("重建成功")
        self.setFixedSize(400, 220)
        self.setStyleSheet("""
            QDialog {
                background-color: #f8f9fa;
                border-radius: 10px;
                border: 1px solid #e0e0e0;
            }
            QLabel#title {
                font-size: 18px;
                font-weight: bold;
                color: #2d3748;
            }
            QLabel#content {
                font-size: 14px;
                color: #4a5568;
                line-height: 1.6;
            }
            QPushButton {
                background-color: #48bb78;
                color: white;
                border: none;
                border-radius: 6px;
                padding: 8px 24px;
                font-size: 14px;
            }
            QPushButton:hover {
                background-color: #38a169;
            }
            QPushButton:pressed {
                background-color: #2f855a;
            }
            QFrame {
                background-color: #e0e0e0;
            }
        """)

        # 主布局
        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(20, 20, 20, 20)
        main_layout.setSpacing(15)

        # 顶部：图标 + 文本
        top_layout = QHBoxLayout()
        top_layout.setSpacing(15)

        # 成功图标（Qt内置）
        icon_label = QLabel()
        pixmap = QPixmap.fromImage(QIcon.fromTheme("dialog-success").pixmap(64, 64).toImage())
        icon_label.setPixmap(pixmap)
        top_layout.addWidget(icon_label, alignment=Qt.AlignTop)

        # 文本内容
        text_layout = QVBoxLayout()
        text_layout.setSpacing(8)

        title_label = QLabel("点云重建成功！")
        title_label.setObjectName("title")
        text_layout.addWidget(title_label)

        content_text = f"""
        重建方法：{method_name}
        顶点数：{vertex_count} 个
        三角形数：{triangle_count} 个
        """
        content_label = QLabel(content_text)
        content_label.setObjectName("content")
        content_label.setWordWrap(True)
        text_layout.addWidget(content_label)

        top_layout.addLayout(text_layout)
        main_layout.addLayout(top_layout)

        # 分割线
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        main_layout.addWidget(line)

        # 底部：确认按钮（居中）
        btn_layout = QHBoxLayout()
        confirm_btn = QPushButton("确定")
        confirm_btn.clicked.connect(self.accept)
        btn_layout.addWidget(confirm_btn, alignment=Qt.AlignCenter)
        main_layout.addLayout(btn_layout)

# ==============================================
# 在这里定义 SegmentationMethodDialog 类（关键位置）
# ==============================================
class SegmentationMethodDialog(QDialog):
    """分割方法选择对话框"""
    def __init__(self, parent=None):  # 注意：之前少了 self 参数，这里要补全！
        super().__init__(parent)
        self.setWindowTitle("选择分割方法")
        self.setFixedSize(300, 200)
        self.selected_method = None

        # 布局
        layout = QVBoxLayout()
        
        # 方法列表
        self.method_list = QListWidget()
        # 添加所有支持的分割方法
        for method in SegmentationMethod.get_all_methods():
            QListWidgetItem(method, self.method_list)
        self.method_list.setCurrentRow(0)  # 默认选中第一个
        layout.addWidget(self.method_list)
        
        # 按钮
        buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
            Qt.Horizontal, self
        )
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        layout.addWidget(buttons)
        
        self.setLayout(layout)

    def get_selected_method(self):
        """获取选中的分割方法"""
        return self.method_list.currentItem().text()

# ==============================================
# 新增重建方法选择对话框（与分割对话框结构一致）
# ==============================================
class ReconstructionMethodDialog(QDialog):
    """重建方法选择对话框"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("选择重建方法")
        self.setFixedSize(300, 200)
        self.selected_method = None

        # 布局
        layout = QVBoxLayout()
        
        # 方法列表
        self.method_list = QListWidget()
        # 添加所有支持的重建方法
        for method in ReconstructionMethod.get_all_methods():
            QListWidgetItem(method, self.method_list)
        self.method_list.setCurrentRow(0)  # 默认选中第一个
        layout.addWidget(self.method_list)
        
        # 按钮
        buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
            Qt.Horizontal, self
        )
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)
        layout.addWidget(buttons)
        
        self.setLayout(layout)

    def get_selected_method(self):
        """获取选中的重建方法"""
        return self.method_list.currentItem().text()


class PointCloudMainWindow(QMainWindow):
    """主窗口（点云处理工作台）"""
    def __init__(self, username: str, role: str):
        super().__init__()
        self.current_username = username  # 当前登录用户名
        self.current_role = role          # 当前用户角色（admin/normal）
        self.setWindowTitle(f"点云处理系统 - {username}（{role}）")
        self.setGeometry(100, 100, WINDOW_SIZE[0], WINDOW_SIZE[1])

        # 点云数据存储
        self.current_point_clouds = None  # 批量点云（N×2048×3）
        self.current_index = 0            # 当前选中的点云索引
        self.max_index = 0                # 最大索引

        # 线程与窗口实例
        self.process_thread = None
        self.waiting_dialog = None

        # 初始化界面
        self.init_ui()
        self.update_index_buttons()  # 初始化翻页按钮状态
        self.update_role_permissions()  # 根据角色设置权限

    def init_ui(self):
        """初始化主界面"""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)

        # 顶部控制栏
        top_control_layout = QHBoxLayout()
        main_layout.addLayout(top_control_layout)

        # 1. 文件选择区域
        self.file_path_label = QLabel("未选择文件")
        self.file_path_label.setStyleSheet("color: #ADD8E6;")
        self.file_path_label.setFixedWidth(300)
        top_control_layout.addWidget(self.file_path_label)

        self.load_button = QPushButton("加载点云文件")
        self.load_button.clicked.connect(self.load_point_cloud)
        self.load_button.setStyleSheet("""
            background-color: #4CAF50; color: white; font-weight: bold; padding: 5px;
        """)
        top_control_layout.addWidget(self.load_button)

        # 2. 弹性间隔
        top_control_layout.addStretch(1)

        # 3. 点云索引导航
        self.prev_button = QPushButton("上一个")
        self.prev_button.clicked.connect(self.prev_point_cloud)
        self.prev_button.setStyleSheet("""
            background-color: #6495ED; color: white; padding: 3px;
        """)
        self.prev_button.setFixedWidth(80)
        top_control_layout.addWidget(self.prev_button)

        self.index_label = QLabel("索引: 0/0")
        self.index_label.setAlignment(Qt.AlignCenter)
        self.index_label.setFixedWidth(80)
        self.index_label.setStyleSheet("color: white;")
        top_control_layout.addWidget(self.index_label)

        self.next_button = QPushButton("下一个")
        self.next_button.clicked.connect(self.next_point_cloud)
        self.next_button.setStyleSheet("""
            background-color: #6495ED; color: white; padding: 3px;
        """)
        self.next_button.setFixedWidth(80)
        top_control_layout.addWidget(self.next_button)

        # 4. 弹性间隔
        top_control_layout.addStretch(1)

        # 5. 核心功能按钮（补全/分割/重建）
        self.complete_button = QPushButton("点云补全")
        self.complete_button.clicked.connect(self.execute_completion)
        self.complete_button.setStyleSheet("""
            background-color: #FFC107; color: black; font-weight: bold; padding: 5px;
        """)
        top_control_layout.addWidget(self.complete_button)

        self.segment_button = QPushButton("点云分割")
        self.segment_button.clicked.connect(self.execute_segmentation)
        self.segment_button.setStyleSheet("""
            background-color: #2196F3; color: white; font-weight: bold; padding: 5px;
        """)
        top_control_layout.addWidget(self.segment_button)

        self.reconstruct_button = QPushButton("点云重建")
        self.reconstruct_button.clicked.connect(self.execute_reconstruction)
        self.reconstruct_button.setStyleSheet("""
            background-color: #9C27B0; color: white; font-weight: bold; padding: 5px;
        """)
        top_control_layout.addWidget(self.reconstruct_button)

        # 主显示区域（左右分屏）
        display_splitter = QSplitter(Qt.Horizontal)
        main_layout.addWidget(display_splitter)

        # 左侧：原始点云面板
        original_panel = QWidget()
        original_layout = QVBoxLayout(original_panel)
        original_layout.setContentsMargins(0, 0, 0, 0)
        original_layout.addWidget(QLabel("原始点云"))
        self.original_viewer = PointCloudViewer()
        original_layout.addWidget(self.original_viewer)
        display_splitter.addWidget(original_panel)

        # 右侧：处理结果面板
        result_panel = QWidget()
        result_layout = QVBoxLayout(result_panel)
        result_layout.setContentsMargins(0, 0, 0, 0)
        self.result_title_label = QLabel("处理结果")
        result_layout.addWidget(self.result_title_label)
        self.result_viewer = PointCloudViewer()
        result_layout.addWidget(self.result_viewer)
        display_splitter.addWidget(result_panel)

        # 分屏大小（左右各占一半）
        display_splitter.setSizes([self.width()//2, self.width()//2])

        # 全局样式表
        self.setStyleSheet("""
            QMainWindow { background-color: #2b2b2b; color: white; }
            QLabel { color: white; font-size: 14px; padding: 5px; }
            QPushButton { border-radius: 5px; min-height: 25px; }
            QPushButton:disabled { background-color: #555555; color: #aaaaaa; }
            QSplitter::handle { background-color: #444444; width: 5px; }
            QWidget#original_panel, QWidget#result_panel { border: 1px solid #444444; }
        """)
        original_panel.setObjectName("original_panel")
        result_panel.setObjectName("result_panel")

        # 初始状态：禁用功能按钮
        self.complete_button.setEnabled(False)
        self.segment_button.setEnabled(False)
        self.reconstruct_button.setEnabled(False)

    def load_point_cloud(self):
        """加载点云文件（支持 .pt、.pcd、.npy 格式，支持任意点数）"""
        file_filter = "点云文件 (*.pt *.pcd *.npy);;PyTorch张量 (*.pt);;PCD格式 (*.pcd);;NumPy数组 (*.npy)"
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择点云文件", "", file_filter
        )
        if not file_path:
            return

        self.file_path_label.setText(file_path)
        try:
            file_ext = file_path.lower().split('.')[-1]
            self.current_point_clouds = None
            self.max_index = 0

            # -------------------------- 1. 处理 .pt 格式（PyTorch张量）--------------------------
            if file_ext == 'pt':
                import torch
                data = torch.load(file_path, map_location='cpu')
                if not isinstance(data, torch.Tensor):
                    raise TypeError(f".pt文件内容不是张量，而是 {type(data)}")
                
                # 支持格式：(M,3)→单个点云；(N,M,3)→批量点云；(3,M)→转置为(M,3)
                if data.dim() == 2:
                    if data.shape[1] == 3:
                        # 单个点云：(M,3) → (1,M,3)
                        self.current_point_clouds = data.numpy().reshape(1, -1, 3)
                    elif data.shape[0] == 3:
                        # 转置为 (M,3) → (1,M,3)
                        self.current_point_clouds = data.permute(1, 0).numpy().reshape(1, -1, 3)
                    else:
                        raise ValueError(f".pt张量维度2但不是3维点云，实际形状：{data.shape}")
                
                elif data.dim() == 3:
                    if data.shape[2] == 3:
                        # 批量点云：(N,M,3) → 直接使用
                        self.current_point_clouds = data.numpy()
                    elif data.shape[1] == 3:
                        # 转置为 (N,M,3)
                        self.current_point_clouds = data.transpose(0, 2, 1).numpy()
                    else:
                        raise ValueError(f".pt张量维度3但不是3维点云，实际形状：{data.shape}")
                
                else:
                    raise ValueError(f".pt张量维度不支持（仅支持2/3维），实际维度：{data.dim()}")

            # -------------------------- 2. 处理 .pcd 格式（Open3D标准格式）--------------------------
            elif file_ext == 'pcd':
                import open3d as o3d
                import numpy as np
                pcd = o3d.io.read_point_cloud(file_path)
                if not pcd.has_points():
                    raise ValueError("PCD文件中没有点云数据")
                
                point_cloud = np.asarray(pcd.points)
                if point_cloud.shape[1] != 3:
                    raise ValueError(f"PCD点云不是3维（x,y,z），实际维度：{point_cloud.shape[1]}")
                
                # 单个点云：(M,3) → (1,M,3)
                self.current_point_clouds = point_cloud.reshape(1, -1, 3)

            # -------------------------- 3. 处理 .npy 格式（NumPy数组）--------------------------
            elif file_ext == 'npy':
                import numpy as np
                data = np.load(file_path, allow_pickle=False)
                if not isinstance(data, np.ndarray):
                    raise TypeError(f".npy文件内容不是数组，而是 {type(data)}")
                
                # 支持格式：(M,3)→单个点云；(N,M,3)→批量点云；(3,M)→转置为(M,3)
                if data.ndim == 2:
                    if data.shape[1] == 3:
                        self.current_point_clouds = data.reshape(1, -1, 3)
                    elif data.shape[0] == 3:
                        self.current_point_clouds = data.T.reshape(1, -1, 3)
                    else:
                        raise ValueError(f".npy数组维度2但不是3维点云，实际形状：{data.shape}")
                
                elif data.ndim == 3:
                    if data.shape[2] == 3:
                        self.current_point_clouds = data
                    elif data.shape[1] == 3:
                        self.current_point_clouds = data.transpose(0, 2, 1)
                    else:
                        raise ValueError(f".npy数组维度3但不是3维点云，实际形状：{data.shape}")
                
                else:
                    raise ValueError(f".npy数组维度不支持（仅支持2/3维），实际维度：{data.ndim}")

            # -------------------------- 不支持的格式 --------------------------
            else:
                raise ValueError(f"不支持的文件格式：.{file_ext}（仅支持 .pt、.pcd、.npy）")

            # 验证最终格式（必须是 (N,M,3)）
            if self.current_point_clouds is None:
                raise RuntimeError("点云数据加载后为空，请检查文件内容")
            if self.current_point_clouds.ndim != 3 or self.current_point_clouds.shape[2] != 3:
                raise RuntimeError(f"点云格式错误，最终形状：{self.current_point_clouds.shape}（应为 (N,M,3)）")

            # 更新索引和显示
            self.max_index = self.current_point_clouds.shape[0] - 1
            self.current_index = 0
            self.display_current_point_cloud()
            # 启用功能按钮
            self.complete_button.setEnabled(True)
            self.segment_button.setEnabled(True)
            self.reconstruct_button.setEnabled(True)
            self.update_index_buttons()
            
            # 友好提示（显示点数信息）
            single_pc_count = self.current_point_clouds[self.current_index].shape[0]
            dialog = SuccessLoadDialog(
                point_cloud_count=self.current_point_clouds.shape[0],
                file_ext=file_ext,
                single_pc_count=single_pc_count,
                parent=self
            )
            dialog.exec_()

            # 保留原有的打印日志
            print(f"[加载成功] 文件：{file_path}，格式：.{file_ext}，点云数量：{self.current_point_clouds.shape[0]}，单云点数：{single_pc_count}")

        except Exception as e:
            error_msg = f"文件加载失败：\n{str(e)}\n\n" \
                        f"格式要求参考：\n" \
                        f"- .pt：(M,3) 或 (N,M,3) 3维张量\n" \
                        f"- .pcd：标准3维点云（任意点数）\n" \
                        f"- .npy：(M,3) 或 (N,M,3) 3维数组"
            QMessageBox.critical(self, "加载失败", error_msg)
            self.file_path_label.setText(f"加载失败：{str(e)[:50]}...")
            self.current_point_clouds = None
            self.complete_button.setEnabled(False)
            self.segment_button.setEnabled(False)
            self.reconstruct_button.setEnabled(False)
            print(f"[加载失败] 文件：{file_path}，错误：{str(e)}")

    # 删除之前的 _unify_point_count 方法（不再需要）

    def display_current_point_cloud(self):
        """显示当前索引的点云"""
        if self.current_point_clouds is None:
            return
        if 0 <= self.current_index <= self.max_index:
            point_cloud = self.current_point_clouds[self.current_index]
            self.original_viewer.set_point_cloud(point_cloud)
            self.index_label.setText(f"索引: {self.current_index}/{self.max_index}")
            self.result_viewer.set_point_cloud(None)  # 清空结果视图
        else:
            self.original_viewer.set_point_cloud(None)
            self.result_viewer.set_point_cloud(None)

    def update_index_buttons(self):
        """更新翻页按钮状态"""
        self.prev_button.setEnabled(self.current_point_clouds is not None and self.current_index > 0)
        self.next_button.setEnabled(self.current_point_clouds is not None and self.current_index < self.max_index)

    def prev_point_cloud(self):
        """上一个点云"""
        if self.current_index > 0:
            self.current_index -= 1
            self.display_current_point_cloud()
            self.update_index_buttons()

    def next_point_cloud(self):
        """下一个点云"""
        if self.current_index < self.max_index:
            self.current_index += 1
            self.display_current_point_cloud()
            self.update_index_buttons()

    def execute_completion(self):
        """执行点云补全"""
        self._run_process_task(
            task_type="completion",
            thread_cls=CompletionThread,
            success_signal="completion_finished",
            error_signal="completion_error",
            result_title="补全点云"
        )

    def execute_segmentation(self):
        """执行点云分割（新增方法选择）"""
        if self.current_point_clouds is None:
            QMessageBox.warning(self, "警告", "请先加载点云文件！")
            return

        # 弹出分割方法选择对话框（这里会用到上面定义的 SegmentationMethodDialog）
        method_dialog = SegmentationMethodDialog(self)
        if method_dialog.exec_() != QDialog.Accepted:
            return  # 用户取消选择
        selected_method = method_dialog.get_selected_method()

        # 禁用按钮，防止重复点击
        self._set_buttons_enabled(False)

        # 获取当前选中的点云
        current_pc = self.current_point_clouds[self.current_index]

        # 显示等待窗口
        self.waiting_dialog = WaitingDialog(message=f"正在执行{selected_method}...", parent=self)
        self.waiting_dialog.show()

        # 创建并启动分割线程（传入选中的方法）
        self.process_thread = SegmentationThread(current_pc, selected_method)
        # 绑定信号（注意：信号参数变为 (points, colors, labels)）
        self.process_thread.segmentation_finished.connect(self._handle_segmentation_success)
        self.process_thread.segmentation_error.connect(self._handle_task_error)
        self.process_thread.start()

    # 修改 execute_reconstruction 方法（新增方法选择）
    def execute_reconstruction(self):
        """执行点云重建（支持多种方法选择）"""
        if self.current_point_clouds is None:
            QMessageBox.warning(self, "警告", "请先加载点云文件！")
            return

        # 弹出重建方法选择对话框
        method_dialog = ReconstructionMethodDialog(self)
        if method_dialog.exec_() != QDialog.Accepted:
            return  # 用户取消选择
        selected_method = method_dialog.get_selected_method()

        # 禁用按钮，防止重复点击
        self._set_buttons_enabled(False)

        # 获取当前选中的点云
        current_pc = self.current_point_clouds[self.current_index]

        # 显示等待窗口
        self.waiting_dialog = WaitingDialog(message=f"正在执行{selected_method}...", parent=self)
        self.waiting_dialog.show()

        # 创建并启动重建线程（传入选中的方法）
        self.process_thread = ReconstructionThread(current_pc, selected_method)
        # 绑定信号（接收网格、顶点、方法名称）
        self.process_thread.reconstruction_finished.connect(self._handle_reconstruction_success)
        self.process_thread.reconstruction_error.connect(self._handle_task_error)
        self.process_thread.start()

    def _handle_reconstruction_success(self, mesh, vertices, method_name):
        """重建成功回调（显示网格模型）"""
        # 关闭等待窗口
        if self.waiting_dialog:
            self.waiting_dialog.close()
            self.waiting_dialog = None

        # 显示重建结果（右侧视图：网格模型）
        self.result_viewer.set_mesh(mesh)
        # 更新结果标题（显示重建方法和网格信息）
        vertex_count = len(vertices)
        triangle_count = len(mesh.triangles)
        self.result_title_label.setText(
            f"重建结果（{method_name}）- 顶点数：{vertex_count}，三角形数：{triangle_count}"
        )   

            # 显示自定义成功对话框
        dialog = SuccessReconstructDialog(
            method_name=method_name,
            vertex_count=vertex_count,
            triangle_count=triangle_count,
            parent=self
        )
        dialog.exec_()

        # 启用功能按钮
        self._set_buttons_enabled(True)

    # _run_process_task 方法（兼容原有其他任务）
    def _run_process_task(self, task_type, thread_cls, success_signal, error_signal, result_title):
        """通用任务执行函数（复用线程逻辑）"""
        if self.current_point_clouds is None:
            QMessageBox.warning(self, "警告", "请先加载点云文件！")
            return

        # 禁用按钮，防止重复点击
        self._set_buttons_enabled(False)

        # 获取当前选中的点云
        current_pc = self.current_point_clouds[self.current_index]

        # 显示等待窗口
        self.waiting_dialog = WaitingDialog(message=f"正在执行{task_type}...", parent=self)
        self.waiting_dialog.show()

        # 创建并启动线程
        self.process_thread = thread_cls(current_pc)
        # 绑定信号
        if success_signal == "segmentation_finished":
            # 分割任务使用专门的成功回调
            self.process_thread.segmentation_finished.connect(self._handle_segmentation_success)
        else:
            # 其他任务使用通用回调
            getattr(self.process_thread, success_signal).connect(
                lambda *args: self._handle_task_success(args, result_title)
            )
        getattr(self.process_thread, error_signal).connect(self._handle_task_error)
        self.process_thread.start()

    def _handle_task_success(self, result_args, result_title):
        """任务成功回调"""
        # 关闭等待窗口
        if self.waiting_dialog:
            self.waiting_dialog.close()
            self.waiting_dialog = None

        # 显示结果（不同任务结果格式不同）
        if len(result_args) == 1:
            # 补全/重建结果（仅点云）
            result_pc = result_args[0]
            self.result_viewer.set_point_cloud(result_pc)
        elif len(result_args) == 2:
            # 分割结果（点云+标签，后续可扩展彩色显示）
            result_pc, labels = result_args
            self.result_viewer.set_point_cloud(result_pc)

        # 更新结果标题
        self.result_title_label.setText(result_title)

        # 显示成功提示
        SuccessDialog(
            title="操作成功",
            content=f"{result_title}已完成，可在右侧视图查看结果（支持旋转/缩放）",
            parent=self
        ).exec_()

        # 启用按钮
        self._set_buttons_enabled(True)

    def _handle_task_error(self, error_msg):
        """任务失败回调"""
        # 关闭等待窗口
        if self.waiting_dialog:
            self.waiting_dialog.close()
            self.waiting_dialog = None

        # 显示错误提示
        QMessageBox.critical(self, "操作失败", f"任务执行失败：{error_msg}")

        # 启用按钮
        self._set_buttons_enabled(True)

    def _set_buttons_enabled(self, enabled):
        """设置所有功能按钮的启用状态"""
        self.load_button.setEnabled(enabled)
        self.complete_button.setEnabled(enabled)
        self.segment_button.setEnabled(enabled)
        self.reconstruct_button.setEnabled(enabled)
        self.update_index_buttons()

    def update_role_permissions(self):
        """根据用户角色设置权限（后续可扩展）"""
        if self.current_role == "admin":
            # 管理员权限：显示所有功能（如服务器配置、用户管理）
            print(f"管理员 {self.current_username} 已登录，启用所有权限")
        else:
            # 普通用户权限：隐藏高级功能
            print(f"普通用户 {self.current_username} 已登录，启用基础权限")

        # ==============================================
    # 新增 _handle_segmentation_success 方法（在 PointCloudMainWindow 类内部）
    # ==============================================
    def _handle_segmentation_success(self, points, colors, labels):
        """分割成功回调（显示彩色点云）"""
        # 关闭等待窗口
        if self.waiting_dialog:
            self.waiting_dialog.close()
            self.waiting_dialog = None

        # 显示彩色分割结果（右侧视图）
        self.result_viewer.set_point_cloud(points, colors)
        # 更新结果标题（显示分割方法和部件数量）
        max_label = labels.max()
        part_count = max_label + 1 if max_label >= 0 else 0
        noise_count = np.sum(labels == -1)
        self.result_title_label.setText(f"分割结果（{self.process_thread.segment_method}）- 部件数：{part_count}，噪声点：{noise_count}")

        # 显示成功提示
        SuccessDialog(
            title="分割成功",
            content=f"成功使用{self.process_thread.segment_method}分割出 {part_count} 个部件\n噪声点数量：{noise_count}",
            parent=self
        ).exec_()

        # 启用按钮
        self._set_buttons_enabled(True)

